﻿// ———————————————————————–
// <copyright file="AWS_ValueListDAL.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// AWS_ValueListDAL.cs - Data Access Layer For the Rules to Be Used To Route EDXL-DE Messages
// Project: EDXLSharp_AWSRouter - ICNETDAL
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2013
// Author:      Brian Wilkins The MITRE Corporation
/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using EDXLSharp;
using ICNETServices;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.SQS;
using Amazon.SQS.Model;
using Amazon.Util;
using ICNETSerializers;
using System.Xml.Serialization;
using System.Xml;
using System.IO;


namespace ICNETDAL
{
  /// <summary>
  /// This class contains methods that are used to retrieve and modify the rules that route EDXL-DE messages. 
  /// </summary>
  public static class AWSValueListDAL
  {
    //TODO: Add support for checking the existance of a value in a ValueList
    //TODO: Add feature to add/remove individual values from an existing ValueList
    #region Public Member Functions
    private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    /// <summary>
    /// Returns all of the ValueLists from the ValueLists table
    /// </summary>
    /// <returns>All ValueLists</returns>
    private static ValLists GetAllLists()
    {
      ValLists VLists = new ValLists();

      ScanResponse response = null;

      try
      {
        Dictionary<string, AttributeValue> lastEvaluatedKey = null;
        do
        {
          ScanRequest req = new ScanRequest();
          req.TableName = AWSConstants.ValueListsProcessing.DynamoInfo.TableName;

          if (lastEvaluatedKey != null)
          {
            req.ExclusiveStartKey = lastEvaluatedKey;
          }
          
          response = AWSUtils.DynamoClient.Scan(req);

          lastEvaluatedKey = response.LastEvaluatedKey;

          if (!LoadValListFromDBItems(VLists, response.Items))
          {
            // We errored out an should return null
            return null;
          }


        } while (lastEvaluatedKey != null && lastEvaluatedKey.Count != 0);


        return VLists;
      }
      catch (Exception e)
      {
        Log.Error("Error scanning from Dynamo ValueLists table", e);
        return null;
      }
    }

    /// <summary>
    /// Returns only the ValueList that matching Urn from the ValueLists table
    /// </summary>
    /// <param name="ValListUrn">URN of the ValueList to return</param>
    /// <returns>All matching ValueLists</returns>
    private static ValLists GetOneList(string ValListUrn)
    {
      ValLists VLists = new ValLists();

      QueryResponse response = null;

      try
      {
        Dictionary<string, AttributeValue> lastEvaluatedKey = null;
        do
        {
          QueryRequest req = new QueryRequest();
          req.TableName = AWSConstants.ValueListsProcessing.DynamoInfo.TableName;

          Dictionary<string, Condition> keyConditions = new Dictionary<string, Condition>();
          Condition cond = new Condition();

          List<AttributeValue> attvalList = new List<AttributeValue>();

          AttributeValue attval = new AttributeValue();
          attval.S = ValListUrn;

          attvalList.Add(attval);

          cond.AttributeValueList = attvalList;
          cond.ComparisonOperator = "EQ";

          keyConditions.Add("valueListUrn", cond);
          req.KeyConditions = keyConditions;
          
          req.ExclusiveStartKey = lastEvaluatedKey;

          response = AWSUtils.DynamoClient.Query(req);

          lastEvaluatedKey = response.LastEvaluatedKey;

          if (!LoadValListFromDBItems(VLists, response.Items))
          {
            // We errored out an should return null
            return null;
          }


        } while (lastEvaluatedKey != null && lastEvaluatedKey.Count != 0);


        return VLists;
      }
      catch (Exception e)
      {
        Log.Error("Error scanning from dynamo Rules table", e);
        return null;
      }
    }

    /// <summary>
    /// Takes a ValueList urn and looks up the corresponding ValList (serializer).
    /// If the valuelist urn is empty, null, or whitespace, all ValueLists will be returned
    /// </summary>
    /// <param name="ValListUrn">ValueList URN - optional</param>
    /// <returns>the ValueList</returns>
    public static ValLists GetValueList(string ValListUrn)
    {
      if (!string.IsNullOrWhiteSpace(ValListUrn))
      {
        return GetOneList(ValListUrn);
      }
      else
      {
        return GetAllLists();
      }
    }

    /// <summary>
    /// Takes a ValueList urn and value and looks up the corresponding ValueList(serializer).
    /// If the valuelist urn is empty, null, or whitespace, all rules will be returned
    /// If the valuelist value is empty, null, or whitespace, all rules for the ValueList urn will be returned.
    /// </summary>
    /// <param name="ValListUrn">ValueList URN - optional</param>
    /// <returns>the ValueList xml</returns>
    public static XElement GetValueListXML(string ValListUrn)
    {
      return ValListsHelper.ToXElement(GetValueList(ValListUrn));
    }

    /// <summary>
    /// Creates the Value List in the ValueList table
    /// </summary>
    /// <param name="TheVLists">The ValueList that should be added to the database</param>
    /// <returns> IF the inputted rule was properly formatted, it will be returned as an XML object with a new GUID
    /// If the inputted rule is improperly formatted, then an XML object will be returned, but not added to the database</returns>
    public static XElement AddValueList(ValLists TheVLists)
    {
      XElement retVal = new XElement("Response");

      List<Dictionary<string, AttributeValue>> putItems = new List<Dictionary<string,AttributeValue>>();

      //make sure the valuelist wrapper and urn and values aren't null
      if (TheVLists != null &&  TheVLists.ValueLists != null )
      {
        if (TheVLists.ValueLists.Count >= 1)
        {
          putItems = LoadValueListToDBItems(TheVLists);
        }
        else //no rules
        {
          retVal.Add(new XElement("Status", "Error"));
          retVal.Add(new XElement("Info", "Missing ValueLists"));
          return retVal;
        }
      }
      else
      {
        retVal.Add(new XElement("Status", "Error"));
        retVal.Add(new XElement("Info", "Missing ValueLists"));
        return retVal;
      }

      //now that we have at least one item...add to DB
      if (putItems.Count == 1) //do a single write
      {
        PutItemRequest putRequest = new PutItemRequest();
        putRequest.TableName = AWSConstants.ValueListsProcessing.DynamoInfo.TableName;

        try
        {
          PutItemResponse putResponse = AWSUtils.DynamoClient.PutItem(putRequest);
          if (putResponse.HttpStatusCode == System.Net.HttpStatusCode.OK)
          {
            Log.Info("ValueList Added to Dynamo");
            retVal.Add(new XElement("Status", "Success"));
            retVal.Add(new XElement("Info", ValListsHelper.ToString(TheVLists)));
          }
          else
          {
            Log.Error("Dynamo Error" + putResponse.HttpStatusCode.ToString());
            retVal.Add(new XElement("Status", "Error"));
            retVal.Add(new XElement("Info", "Dynamo Error: " + putResponse.HttpStatusCode.ToString()));
          }
        }
        catch (Exception e)
        {
          Log.Error("Error writing ValueList to dynamo", e);
          retVal.Add(new XElement("Status", "Error"));
          retVal.Add(new XElement("Info", "Error writing ValueList to dynamo: " + e.ToString()));
        }
      }
      else if (putItems.Count > 1) //do a batch write
      {
        BatchWriteItemRequest batchRequest = new BatchWriteItemRequest();

        Dictionary<string, List<WriteRequest>> requestItems = new Dictionary<string, List<WriteRequest>>();
        List<WriteRequest> writeRequests = new List<WriteRequest>();

        foreach (Dictionary<string, AttributeValue> item in putItems)
        {
          writeRequests.Add(new WriteRequest(new PutRequest(item)));
        }
        requestItems.Add(AWSConstants.ValueListsProcessing.DynamoInfo.TableName, writeRequests);
        batchRequest.RequestItems = requestItems;
        try
        {
          BatchWriteItemResponse batchResponse = AWSUtils.DynamoClient.BatchWriteItem(batchRequest);
          if (batchResponse.HttpStatusCode == System.Net.HttpStatusCode.OK)
          {
            Log.Info(putItems.Count.ToString() + " ValueLists Added to Dynamo");
            retVal.Add(new XElement("Status", "Success"));
            retVal.Add(new XElement("Info", putItems.Count.ToString() + " ValueLists Added"));
          }
          else
          {
            Log.Error("Dynamo Error" + batchResponse.HttpStatusCode.ToString());
            retVal.Add(new XElement("Status", "Error"));
            retVal.Add(new XElement("Info", "Dynamo Error: " + batchResponse.HttpStatusCode.ToString()));
          }
        }
        catch (Exception e)
        {
          Log.Error("Error writing ValueLists to dynamo", e);
          retVal.Add(new XElement("Status", "Error"));
          retVal.Add(new XElement("Info", "Error writing ValueLists to dynamo: " + e.ToString()));
        }
      }
      
      return retVal;
    }

    /// <summary>
    /// Deletes a routing rule from the ValueList Database
    /// </summary>
    /// <param name="ValListUrn">ValueList Urn of the routing rule that should be deleted</param>
    /// <param name="ValListValue">ValueList VAlue of the routing rule that should be deleted - optional</param>
    public static XElement DeleteValueList(string ValListUrn, string ValListValue)
    {
      XElement retVal = new XElement("Response");

      DeleteItemRequest deleteRequest = new DeleteItemRequest();
      deleteRequest.TableName = AWSConstants.ValueListsProcessing.DynamoInfo.TableName;

      Dictionary<string, AttributeValue> deleteKeys = new Dictionary<string, AttributeValue>();
      AttributeValue value;

      if (!string.IsNullOrWhiteSpace(ValListUrn) && !string.IsNullOrWhiteSpace(ValListValue))
      {
        value = new AttributeValue();
        value.S = ValListUrn;
        deleteKeys.Add(AWSConstants.ValueListsProcessing.DynamoInfo.HashName, value);

        value = new AttributeValue();
        value.S = ValListValue;
        deleteKeys.Add("value", value);
      }
      else if (!string.IsNullOrWhiteSpace(ValListUrn))
      {
        value = new AttributeValue();
        value.S = ValListUrn;
        deleteKeys.Add(AWSConstants.RulesProcessing.DynamoInfo.HashName, value);
      }

      try
      {
        DeleteItemResponse deleteResponse = AWSUtils.DynamoClient.DeleteItem(deleteRequest);
        if (deleteResponse.HttpStatusCode == System.Net.HttpStatusCode.OK)
        {
          Log.Info("Deleting ValueList");
          retVal.Add(new XElement("Status", "Success"));
          retVal.Add(new XElement("Info", "Urn = " + ValListUrn + ", Value = " + ValListValue));
        }
        else
        {
          Log.Error("Dynamo Error" + deleteResponse.HttpStatusCode.ToString());
          retVal.Add(new XElement("Status", "Error"));
          retVal.Add(new XElement("Info", "Dynamo Error: " + deleteResponse.HttpStatusCode.ToString()));
        }
      }
      catch (Exception e)
      {
        Log.Error("Error deleting ValueList in Dynamo", e);
        retVal.Add(new XElement("Status", "Error"));
        retVal.Add(new XElement("Info", "Error deleting ValueList in Dynamo: " + e.ToString()));
      }

      return retVal;
    }

    #endregion

    #region Protected Member Functions

    #endregion

    #region Private Member Functions

    /// <summary>
    /// Loads information from a set of ValueList table rows
    /// </summary>
    /// <param name="ValueList">The ValueList item.</param>
    /// <param name="Items">ValueList table rows</param>
    /// <returns>ValueList or null if it failed.</returns>
    private static bool LoadValListFromDBItems(ValLists VLists, List<Dictionary<string, AttributeValue>> Items)
    {

      foreach (Dictionary<string, AttributeValue> item in Items)
      {
        ValList aVList = new ValList();

        AttributeValue ruleValue = item["valueListUrn"];
        aVList.ValueListUrn = ruleValue.S;

        ruleValue = item["value"];
        aVList.Value = ruleValue.SS;

        VLists.ValueLists.Add(aVList);
      }
      return true;
    }

    private static List<Dictionary<string, AttributeValue>> LoadValueListToDBItems(ValLists VLists)
    {
      List<Dictionary<string, AttributeValue>> items = new List<Dictionary<string, AttributeValue>>();

      foreach (ValList aVList in VLists.ValueLists)
      {
        Dictionary<string, AttributeValue> item = new Dictionary<string, AttributeValue>();

        //add valuelist urn
        AttributeValue dynamovalue = new AttributeValue();
        dynamovalue.S = aVList.ValueListUrn;
        item.Add(AWSConstants.ValueListsProcessing.DynamoInfo.HashName, dynamovalue);

        //add valuelist value
        dynamovalue = new AttributeValue();
        dynamovalue.SS = aVList.Value;
        item.Add("value", dynamovalue);

        items.Add(item);
      }

      return items;
    }

    #endregion         
  }
}
